package model.map;

import java.util.LinkedList;
import java.util.List;

public class GameMap 
{
	private HexTile[][] map;
	private List<GameTile> startingLocations;
	private int sLocPointer;
	
	public GameMap(HexTile[][] m)
	{
		map = m;
		sLocPointer = 0;
		startingLocations = new LinkedList<GameTile>();
		
		linkTiles();
	}
	
	public void addStartingLocation(int x, int y)
	{		
		if (!startingLocations.contains(map[x][y]))
			startingLocations.add(map[x][y]);
	}
	
	public boolean hasNextStartingLocation()
	{
		return sLocPointer < startingLocations.size();
	}
	
	public GameTile getNextStartingLocation()
	{
		return startingLocations.get(sLocPointer++);
	}
	
	public void resetStartingLocationPointer()
	{
		sLocPointer = 0;
	}
	
	public int getMapHeight()
	{
		return map[0].length;
	}
	
	public int getMapLength()
	{
		return map.length;
	}
	
	private void linkTiles()
	{
		for (int i = 0; i < getMapLength(); i++)
		{
			
				for (int j = 0; j < getMapHeight(); j++)
				{
					if ((i + 1) < getMapLength())
					{
						if (i % 2 == 0)
						{
							if ((j-1) >= 0)
							{
								map[i][j].addNeighbor(Direction.NE, map[i+1][j-1]);
								map[i+1][j-1].setRelativeCoord(map[i][j].getRelativeX(), map[i][j].getRelativeY(), Direction.NE);
								map[i+1][j-1].addNeighbor(Direction.SW, map[i][j]);
								//System.out.println(i + ", " + j + "->" + (i+1) + ", " + (j-1));
								//System.out.println(map[i][j].getNeighborAt(Direction.NE) = map[i+1][j-1]);
							}
							
							map[i][j].addNeighbor(Direction.SE, map[i+1][j]);
							map[i+1][j].setRelativeCoord(map[i][j].getRelativeX(), map[i][j].getRelativeY(), Direction.SE);
							map[i+1][j].addNeighbor(Direction.NW, map[i][j]);
							
						}
						else
						{
							map[i][j].addNeighbor(Direction.NE, map[i+1][j]);
							map[i+1][j].setRelativeCoord(map[i][j].getRelativeX(), map[i][j].getRelativeY(), Direction.NE);
							map[i+1][j].addNeighbor(Direction.SW, map[i][j]);
							
							if ((j+1) < getMapHeight())
							{
								map[i][j].addNeighbor(Direction.SE, map[i+1][j+1]);
								map[i+1][j+1].setRelativeCoord(map[i][j].getRelativeX(), map[i][j].getRelativeY(), Direction.SE);
								map[i+1][j+1].addNeighbor(Direction.NW, map[i][j]);
								
							}
							
						}
					}
					
					if ((j+1) < getMapHeight())
					{
						map[i][j].addNeighbor(Direction.S, map[i][j+1]);
						map[i][j+1].setRelativeCoord(map[i][j].getRelativeX(), map[i][j].getRelativeY(), Direction.S);
						map[i][j+1].addNeighbor(Direction.N, map[i][j]);
					}
				}
			
		}
	}
	
	public GameTile getTileAt(int x, int y)
	{
		if (x >= map.length || x < 0 || y >= map[0].length || y < 0)
			throw new ArrayIndexOutOfBoundsException("Not a valid map coordinate!");
		return map[x][y]; 
	}
	
	public List<GameTile> getTilesAround(GameTile t, int radius)
	{
		if (t == null)
			throw new IllegalArgumentException("the given tile is null");
		if (radius < 0)
			throw new IllegalArgumentException("radius cannot be a value less than 0");
		
		List<GameTile> l = new LinkedList<GameTile>(); 
		gTAHelper(t, radius, l);
		unmarkAll();
		return l;
	}

	private void gTAHelper(GameTile t, int radius, List<GameTile> l)
	{
		t.mark();
		
		l.add(t);
		
		Direction d = Direction.N;
		
		if (radius <= 0)
			return;
		
		do
		{
			if (t.hasNeighborAt(d) && !t.getNeighborAt(d).isMarked())
				gTAHelper(t.getNeighborAt(d),radius-1,l);
			
			d = d.getClockwise();
		} while (d != Direction.N);
	}
	
	private void unmarkAll()
	{
		for (int i = 0; i < map.length; i++)
			for (int j = 0; j < map[i].length; j++)
				map[i][j].unmark();
	}
	
	public int getDistanceBetween(GameTile from, GameTile to)
	{
		return (Math.abs(from.getRelativeX() - to.getRelativeX()) + Math.abs(from.getRelativeY() - to.getRelativeY()) + Math.abs(from.getRelativeZ() - to.getRelativeZ()))/2;
	}
	
	public List<Direction> getDirections(GameTile from, GameTile to)
	{
		List<Direction> l = new LinkedList<Direction>();
		if (from != to)
			gDirHelper(from, to, l);
		return l;
	}
	
	private void gDirHelper(GameTile from, GameTile to, List<Direction> l)
	{
		System.out.println("From:");
		System.out.println (from);
		System.out.println("To:");
		System.out.println (to);
		
		if (from == to)
			return;
		Direction d = getDirectionTo(from, to);
		if (d == Direction.C)
			throw new IllegalStateException("Fatal Error: Either from and to are " +
											"not on the same map or the coordinates " +
											"of two different tiles are alike!");
		l.add(d);
		gDirHelper(from.getNeighborAt(d), to, l);
	}
	
	public Direction getDirectionTo (GameTile from, GameTile to)
	{
		int fromY = from.getRelativeY();
		int fromZ = from.getRelativeZ();
		
		int toY = to.getRelativeY();
		int toZ = to.getRelativeZ();
		
		System.out.println("FromZ:" + fromZ + ", ToZ: " + toZ);
		
		if (fromZ < toZ)
		{
			
			if (fromY > toY)
			{
				return Direction.NE;
			}
			else
			{
				return Direction.SE;
			}
		}
		else if (fromZ > toZ)
		{
			if (fromY < toY)
			{
				return Direction.SW;
			}
			else
			{
				return Direction.NW;
			}
		}
		else
		{
			if (fromY > toY)
			{
				return Direction.N;
			}
			else if (fromY < toY)
			{
				return Direction.S;
			}
			else
			{
				return Direction.C;
			}
		}
	}
}